﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Windows.Forms;
using xn;

namespace KinectCore.Gui.Controller
{
    public class SilhouettePainter
    {
        private readonly Color[] colors = { Color.Red, Color.Blue, Color.ForestGreen, Color.Yellow, Color.Orange, Color.Purple, Color.White };
        private readonly Color[] anticolors = { Color.Green, Color.Orange, Color.Red, Color.Purple, Color.Blue, Color.Yellow, Color.Black };
        private readonly int ncolors = 6;


        // OpenNI components
        private Context context;
        private DepthGenerator depth;
        private UserGenerator userGenerator;
        //private SkeletonCapability skeletonCapbility;

        // Drawing components
        private Bitmap bitmap;

        // Threading components
        private Thread readerThread;
        private bool shouldRun;

        // GUI components
        private Control control;

        public SilhouettePainter(Context context, Control control)
        {
            // get our context and depth map generator
            this.context = context;
            this.depth = context.FindExistingNode(NodeType.Depth) as DepthGenerator;
            if (this.depth == null)
            {
                throw new Exception("Viewer must have a depth node!");
            }

            // get a user generator
            this.userGenerator = new UserGenerator(this.context);
            this.userGenerator.NewUser += new UserGenerator.NewUserHandler(userGenerator_NewUser);
            this.userGenerator.LostUser += new UserGenerator.LostUserHandler(userGenerator_LostUser);
            this.userGenerator.StartGenerating();

            MapOutputMode mapMode = this.depth.GetMapOutputMode();

            // init the bitmap
            this.bitmap = new Bitmap((int)mapMode.nXRes, (int)mapMode.nYRes/*, System.Drawing.Imaging.PixelFormat.Format24bppRgb*/);

            // init the thread
            this.shouldRun = true;
            this.readerThread = new Thread(ReaderThread);

            // remember the control
            this.control = control;
            // register the control paint event
            control.Paint += new PaintEventHandler(control_Paint);

        }

        ~SilhouettePainter()
        {
            Stop();
        }

        // Event called when control is repainted
        private void control_Paint(object sender, PaintEventArgs e)
        {
            lock (this.control)
            {
                // draw our buffer bitmap onto the control
                e.Graphics.DrawImage(this.bitmap,
                    0, //this.control.Location.X,
                    0, //this.control.Location.Y,
                    this.control.Size.Width,
                    this.control.Size.Height);
            }
        }

        void userGenerator_NewUser(ProductionNode node, uint id)
        {
            //this.poseDetectionCapability.StartPoseDetection(this.calibPose, id);
        }

        void userGenerator_LostUser(ProductionNode node, uint id)
        {
            //this.joints.Remove(id);
        }

        private unsafe void ReaderThread()
        {
            DepthMetaData depthMD = new DepthMetaData();

            while (this.shouldRun)
            {
                try
                {
                    this.context.WaitOneUpdateAll(this.depth);
                }
                catch (Exception)
                {
                }

                this.depth.GetMetaData(depthMD);

                //CalcHist(depthMD);

                lock (this.control)
                {
                    Rectangle rect = new Rectangle(0, 0, this.bitmap.Width, this.bitmap.Height);
                    BitmapData data = this.bitmap.LockBits(rect, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);


                    // draw pixels
                    ushort* pDepth = (ushort*)this.depth.GetDepthMapPtr().ToPointer();
                    ushort* pLabels = (ushort*)this.userGenerator.GetUserPixels(0).SceneMapPtr.ToPointer();

                    // set pixels
                    for (int y = 0; y < depthMD.YRes; ++y)
                    {
                        byte* pDest = (byte*)data.Scan0.ToPointer() + y * data.Stride;
                        for (int x = 0; x < depthMD.XRes; ++x, ++pDepth, ++pLabels, pDest += 3)
                        {
                            pDest[0] = pDest[1] = pDest[2] = 0;

                            ushort label = *pLabels;
                            if (*pLabels != 0)
                            {
                                Color labelColor = Color.White;
                                if (label != 0)
                                {
                                    labelColor = colors[label % ncolors];
                                }

                                pDest[0] = labelColor.B;
                                pDest[1] = labelColor.G;
                                pDest[2] = labelColor.R;
                            }
                        }
                    }
                    this.bitmap.UnlockBits(data);

                    //Graphics g = Graphics.FromImage(this.bitmap);
                    //uint[] users = this.userGenerator.GetUsers();
                    //foreach (uint user in users)
                    //{


                    //}
                    //g.Dispose();
                }
                

                // cause the control to be redrawn
                control.Invalidate();
            }
        }

        public void Start()
        {
            this.readerThread.Start();
        }

        public void Stop()
        {
            this.shouldRun = false;
            if (this.readerThread != null && this.readerThread.ThreadState != ThreadState.Unstarted)
                this.readerThread.Join();
        }
    }
}
